//ran tao 
//nature of code
//10.30.06
//week 7

//a basic implementation of John Conway's Game of Life CA
//how could this be improved to use object oriented programming?
//think of it as similar to our particle system, with a "cell" class
//to describe each individual cell and a "cellular automata" class
//to describe a collection of cells

//edited from dan shiffman's gol code

int cellsize = 15;
int COLS, ROWS;
//game of life board
int[][] old_board, new_board, colors;

void setup()
{
  size(800, 500);
  smooth();
  //initialize rows, columns and set-up arrays
  COLS = width/cellsize;
  ROWS = height/cellsize;
  old_board = new int[COLS][ROWS];
  new_board = new int[COLS][ROWS];
  colors = new int[COLS][ROWS];
  colorMode(RGB,255,255,255,100);
  background(0);
  //call function to fill array with random values 0 or 1
  initBoard();
  frameRate(30);
}

void draw()
{
  //background(0);
  fill(0,40);
  rectMode(CORNER);
  rect(0,0,width, height);
  grid();
  check();
  render();
  
}

void grid() {
  for (int a=0; a<=COLS; a++) {
    for (int b=0; b<=ROWS; b++) {
      stroke(15);
      noFill();
      rectMode(CENTER);
      rect(a*cellsize, b*cellsize, cellsize, cellsize);
      
    }
  }
}

void check() {
  Random generator = new Random();
  //loop through every spot in our 2D array and check spots neighbors
  for (int x = 0; x < COLS;x++) {
    for (int y = 0; y < ROWS;y++) {
      int nb = 0;
      //Note the use of mod ("%") below to ensure that cells on the edges have "wrap-around" neighbors
      //above row
    if (old_board[(x+COLS-1) % COLS ][(y+ROWS-1) % ROWS ] == 1) { nb++; }
    if (old_board[ x                ][(y+ROWS-1) % ROWS ] == 1) { nb++; }
    if (old_board[(x+1)      % COLS ][(y+ROWS-1) % ROWS ] == 1) { nb++; }
      //middle row
    if (old_board[(x+COLS-1) % COLS ][ y                ] == 1) { nb++; }
    if (old_board[(x+1)      % COLS ][ y                ] == 1) { nb++; }
      //bottom row
    if (old_board[(x+COLS-1) % COLS ][(y+1)      % ROWS ] == 1) { nb++; }
    if (old_board[ x                ][(y+1)      % ROWS ] == 1) { nb++; }
    if (old_board[(x+1)      % COLS ][(y+1)      % ROWS ] == 1) { nb++; }

  
      
      float r = (float) generator.nextGaussian();
      float g = (float) generator.nextGaussian();
      float b = (float) generator.nextGaussian();
      float w = (float) generator.nextGaussian();

      //define standard deviation and mean
      //scale by standard deviation and mean
      //also constrain to between (0,255) since we are dealing with color
 
      float sd =50; float mean = 100;
      r = constrain((r * sd) + mean,0,255);
      sd = 50; mean = 200;
      g = constrain((g * sd) + mean,0,255);
      sd = 50; mean = 150;
      b = constrain((b * sd) + mean,0,255);
      sd = 50; mean = 150;
      w = constrain((w * sd) + mean,0,255);
      
    //RULES OF "LIFE" HERE
    if      ((old_board[x][y] == 1) && (nb <  2)) { new_board[x][y] = 0; colors[x][y] = color(w);  }      //loneliness
    else if ((old_board[x][y] == 1) && (nb >  3)) { new_board[x][y] = 0; colors[x][y] = color(r,0,0); }      //overpopulation
    else if ((old_board[x][y] == 0) && (nb == 3)) { new_board[x][y] = 1; colors[x][y] = color(0,0,b); }      //reproduction
    else                                          { new_board[x][y] = old_board[x][y]; colors[x][y] = color(0,g,0); }  //stasis
    }
  } 
  
}

void render() {

  //RENDER game of life based on "new_board" values
  for ( int i = 0; i < COLS;i++) {
    for ( int j = 0; j < ROWS;j++) {
      if ((new_board[i][j] == 1)) {
       // fill(255);
       fill(colors[i][j]);
        noStroke();
        ellipse(i*cellsize,j*cellsize,cellsize,cellsize);
      }
    }
  }
  //swap old and new game of life boards
  int[][] tmp = old_board;
  old_board = new_board;
  new_board = tmp;
}

//init board with random "alive" squares
void initBoard() {
  background(0);
  for (int i =0;i < COLS;i++) {
    for (int j =0;j < ROWS;j++) {
      if (int(random(5)) <= 2) {
        old_board[i][j] = 1;
      } else {
        old_board[i][j] = 0;
      }
    }
  }
}

void mousePressed() {
  if (mouseX<width && mouseX >0 && mouseY <height && mouseY > 0) {
   int new_x;
  int new_y;
  
  new_x = mouseX/cellsize;
  new_y = mouseY/cellsize;
  old_board[new_x][new_y] = 1;
  check();
  }
}


//re-set board when mouse is pressed
void mouseDragged() {
  if (mouseX<width && mouseX >0 && mouseY <height && mouseY > 0) {
  int new_x;
  int new_y;
  
  new_x = mouseX/cellsize;
  new_y = mouseY/cellsize;
  //old_board[new_x][new_y] = 1;
  new_board[new_x][new_y] = 1;
  render();
  }
}

void keyPressed() {
 if (keyCode == ' ') {
     initBoard();
 } 
  
}
